ποΈGitΠ―ΡΠ°ποΈ
Commit 5855736e1d1d4fcb1f16132752f13314ffe664ef
Parents : c16c33f
Author : Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com>
Signature : Signature validation error
Date : 2026-08-11T08:28:12-07:00
Committer : GitHub <noreply@github.com>
Date : 2026-08-11T15:28:12Z
fix(tak): gate V2 packets on known firmware (#6600)
Changes
2 files changed, 39 insertions(+), 56 deletions(-)
Diff
diff --git a/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/TAKMeshIntegration.kt b/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/TAKMeshIntegration.kt
index b0ae5c5a95..fb1f2be200 100644
--- a/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/TAKMeshIntegration.kt
+++ b/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/TAKMeshIntegration.kt
@@ -165,12 +165,11 @@ class TAKMeshIntegration(
/**
* Determine the outbound TAK protocol version based on the connected radio's firmware version. Evaluated per-send
- * (not cached) so the bridge picks up firmware upgrades during a session without restart. If the firmware version
- * is unavailable (radio not yet handshook), default to V2 β the v2 firmware was released widely enough that
- * defaulting to legacy would be a regression for the common case.
+ * (not cached) so the bridge picks up firmware upgrades during a session without restart. Until the version is
+ * known, use the legacy format which every TAK-capable firmware version supports.
*/
private fun useTakV2(): Boolean {
- val fw = nodeRepository.myNodeInfo.value?.firmwareVersion ?: return true
+ val fw = nodeRepository.myNodeInfo.value?.firmwareVersion ?: return false
return Capabilities(fw).supportsTakV2
}
diff --git a/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKMeshIntegrationTest.kt b/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKMeshIntegrationTest.kt
index cb095060dc..20a4da4703 100644
--- a/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKMeshIntegrationTest.kt
+++ b/core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKMeshIntegrationTest.kt
@@ -202,51 +202,30 @@ class TAKMeshIntegrationTest {
}
private class FakeNodeRepository(firmwareVersion: String? = "2.8.0.0") : NodeRepository {
- private val _myNodeInfo =
- MutableStateFlow(
- firmwareVersion?.let {
- MyNodeInfo(
- myNodeNum = 1,
- hasGPS = false,
- model = null,
- firmwareVersion = it,
- couldUpdate = false,
- shouldUpdate = false,
- currentPacketId = 0L,
- messageTimeoutMsec = 0,
- minAppVersion = 0,
- maxChannels = 8,
- hasWifi = false,
- channelUtilization = 0f,
- airUtilTx = 0f,
- deviceId = null,
- )
- },
- )
+ private val _myNodeInfo = MutableStateFlow(myNodeInfo(firmwareVersion))
override val myNodeInfo: StateFlow<MyNodeInfo?> = _myNodeInfo
fun setFirmwareVersion(version: String?) {
- _myNodeInfo.value =
- version?.let {
- MyNodeInfo(
- myNodeNum = 1,
- hasGPS = false,
- model = null,
- firmwareVersion = it,
- couldUpdate = false,
- shouldUpdate = false,
- currentPacketId = 0L,
- messageTimeoutMsec = 0,
- minAppVersion = 0,
- maxChannels = 8,
- hasWifi = false,
- channelUtilization = 0f,
- airUtilTx = 0f,
- deviceId = null,
- )
- }
+ _myNodeInfo.value = myNodeInfo(version)
}
+ private fun myNodeInfo(firmwareVersion: String?) = MyNodeInfo(
+ myNodeNum = 1,
+ hasGPS = false,
+ model = null,
+ firmwareVersion = firmwareVersion,
+ couldUpdate = false,
+ shouldUpdate = false,
+ currentPacketId = 0L,
+ messageTimeoutMsec = 0,
+ minAppVersion = 0,
+ maxChannels = 8,
+ hasWifi = false,
+ channelUtilization = 0f,
+ airUtilTx = 0f,
+ deviceId = null,
+ )
+
override val ourNodeInfo: StateFlow<Node?> = MutableStateFlow(null)
override val myId: StateFlow<String?> = MutableStateFlow(null)
override val localStats: StateFlow<LocalStats> = MutableStateFlow(LocalStats())
@@ -431,18 +410,25 @@ class TAKMeshIntegrationTest {
// ββ Firmware gating ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@Test
- fun `null firmware defaults to V2 protocol`() = runTest(UnconfinedTestDispatcher()) {
+ fun `unknown firmware uses V1 protocol`() = runTest(UnconfinedTestDispatcher()) {
val h = TestHarness(nodeRepository = FakeNodeRepository(firmwareVersion = null))
h.integration.start(backgroundScope)
- h.serverManager.emitInbound(createPli("test-v2-default"))
+ h.serverManager.emitInbound(createPli("test-v1-until-version-known"))
- // In commonTest without TAKPacket-SDK, v2 path catches and falls back.
- // Verify the code didn't crash and attempted to send.
- if (h.commandSender.sentPackets.isNotEmpty()) {
- val sent = h.commandSender.sentPackets.first()
- assertEquals(PortNum.ATAK_PLUGIN_V2.value, sent.dataType)
- }
+ assertEquals(1, h.commandSender.sentPackets.size)
+ assertEquals(PortNum.ATAK_PLUGIN.value, h.commandSender.sentPackets.single().dataType)
+ }
+
+ @Test
+ fun `V2-capable firmware sends V2 protocol`() = runTest(UnconfinedTestDispatcher()) {
+ val h = TestHarness(nodeRepository = FakeNodeRepository(firmwareVersion = "2.8.0.0"))
+ h.integration.start(backgroundScope)
+
+ h.serverManager.emitInbound(createPli("test-v2-known"))
+
+ assertEquals(1, h.commandSender.sentPackets.size)
+ assertEquals(PortNum.ATAK_PLUGIN_V2.value, h.commandSender.sentPackets.single().dataType)
}
@Test
@@ -452,10 +438,8 @@ class TAKMeshIntegrationTest {
h.serverManager.emitInbound(createPli("test-v1"))
- if (h.commandSender.sentPackets.isNotEmpty()) {
- val sent = h.commandSender.sentPackets.first()
- assertEquals(PortNum.ATAK_PLUGIN.value, sent.dataType)
- }
+ assertEquals(1, h.commandSender.sentPackets.size)
+ assertEquals(PortNum.ATAK_PLUGIN.value, h.commandSender.sentPackets.single().dataType)
}
@Test
Served by rngit 1.5.2 - Generated in 0.15s